#ifndef GST_LinestringErrorChecks_hpp__
#define GST_LinestringErrorChecks_hpp__

#include "buildspec.h"

#include <string>
#include <vector>

#include <boost/ptr_container/ptr_vector.hpp>

#include <json_spirit/json_spirit.h>

namespace GST
{
namespace Geometry
{
namespace GeoTagProfiles
{
struct Linestring2d;
struct MultiLineString2d;
} // namespace GeoTagProfiles

// interfaces:

// represents one error of a linestring
// note: multiple errors of the same type for one linestring are allowed
class LinestringError
{
public:
	virtual ~LinestringError()
	{
	}
	json_spirit::Object toJson() const;
	std::string toString() const;

private:
	// should return a id of the linestring that has an error
	virtual int linestringId() const = 0;
	// should return the type / name of the error
	virtual std::string errorType() const = 0;
	// should return a message with what is wrong
	virtual std::string customErrorMessage() const = 0;
	// can add special information for the error
	virtual void addCustomErrorToJosnObject(json_spirit::Object &obj) const = 0;
};

// represents one error check for a list of linestrings
// note: can also add multiple errors per linestring
// note: nothing forbids to check and add different error kinds from one
//   LinestringErrorCheck, but it is probably clearer to do only one specific
//   check per LinestringErrorCheck type
class GST_API_EXPORT LinestringErrorCheck
{
public:
	virtual ~LinestringErrorCheck()
	{
	}
	// execute the check and add found error(s) to errors
	virtual void doCheck(boost::ptr_vector<LinestringError> &errors,
						 const std::vector<GeoTagProfiles::Linestring2d>
								 &linestrings) const = 0;
};

// implementations:

class LinestringErrorHasMultiPart : public LinestringError
{
public:
	LinestringErrorHasMultiPart(int id);

private:
	virtual int linestringId() const;
	virtual std::string errorType() const;
	virtual std::string customErrorMessage() const;
	virtual void addCustomErrorToJosnObject(json_spirit::Object &obj) const;
	int m_linestringId;
};

class LinestringErrorHasIdenticalVertices : public LinestringError
{
public:
	LinestringErrorHasIdenticalVertices(int id, int identId, int identToId);

private:
	virtual int linestringId() const;
	virtual std::string errorType() const;
	virtual std::string customErrorMessage() const;
	virtual void addCustomErrorToJosnObject(json_spirit::Object &obj) const;
	int m_linestringId;
	int m_identicalVertexId;
	int m_identicalToVertexId;
};

class LinestringErrorHasBackroutingSegment : public LinestringError
{
public:
	LinestringErrorHasBackroutingSegment(int id, int segId, int segOntoId);

private:
	virtual int linestringId() const;
	virtual std::string errorType() const;
	virtual std::string customErrorMessage() const;
	virtual void addCustomErrorToJosnObject(json_spirit::Object &obj) const;
	int m_linestringId;
	int m_backRoutingSegmentId;
	int m_backRoutingOntoSegmentId;
};

class GST_API_EXPORT LinestringErrorCheckHasIdenticalVertices
		: public LinestringErrorCheck
{
public:
	LinestringErrorCheckHasIdenticalVertices();
	LinestringErrorCheckHasIdenticalVertices(double epsilon);
	virtual void doCheck(
			boost::ptr_vector<LinestringError> &errors,
			const std::vector<GeoTagProfiles::Linestring2d> &lineStrings) const;

private:
	double m_epsilon;
};

class GST_API_EXPORT LinestringErrorCheckHasBackroutingSegments
		: public LinestringErrorCheck
{
public:
	LinestringErrorCheckHasBackroutingSegments();
	LinestringErrorCheckHasBackroutingSegments(double epsilon);
	virtual void doCheck(
			boost::ptr_vector<LinestringError> &errors,
			const std::vector<GeoTagProfiles::Linestring2d> &lineStrings) const;

private:
	double m_epsilon;
};

class GST_API_EXPORT LinestringErrorChecks
{
public:
	LinestringErrorChecks(bool doHasMultiPartsCheck = true);
	// takes ownership of check
	void addCheck(LinestringErrorCheck *check);
	void checkIfHasMultiParts(
			boost::ptr_vector<LinestringError> &errors,
			const std::vector<GeoTagProfiles::MultiLineString2d>
					&multilinestrings) const;
	void doChecks(
			boost::ptr_vector<LinestringError> &errors,
			const std::vector<GeoTagProfiles::Linestring2d> &lineStrings) const;

private:
	LinestringErrorChecks(const LinestringErrorChecks &);
	LinestringErrorChecks &operator=(const LinestringErrorChecks &);
	boost::ptr_vector<LinestringErrorCheck> m_checks;
	bool m_doHasMultiPartsCheck;
};

// helper functions:

void toJson(json_spirit::Array &jsonArray,
			const boost::ptr_vector<LinestringError> &errors);

void toStrings(std::vector<std::string> &errorStrings,
			   const boost::ptr_vector<LinestringError> &errors);

// How to implement a custom error check:
//
// - implement LinestringError for your check
// - implement LinestringErrorCheck for your check
//
// don't forget to add your error check to LinestringErrorChecks
} // namespace Geometry
} // namespace GST
#endif // GST_LinestringErrorChecks_hpp__
